using Arcane.Source.Combat;
using Arcane.Source.Utils;
using Godot;

namespace Arcane.Source.Client
{
    public partial class RuneCell : Node2D
    {
        private RuneMatcher matcher;

        private Vector2I cell;
        private int type;
        private int level;
        private float timer = 0;
        public RunePhase? phase;

        private float size;
        private Tween tween;

        public Vector2I Cell => cell;
        public int Col => cell.X;
        public int Row => cell.Y;
        public RunePhase? Phase => phase;

        public RuneCell(RuneMatcher matcher)
        {
            this.matcher = matcher;
        }

        public override void _Ready()
        {
            QueueRedraw();
        }

        public override void _Draw()
        {
            var pixelSize = size / 16.0f;
            var rect = MathUtil.Square(size + 2.0f * pixelSize);
            DrawTextureRect(matcher.RuneTextures[type, level], rect, false);
        }

        public override void _Notification(int what)
        {
            if (what == NotificationPredelete) matcher = null;
        }

        public override void _Process(double delta)
        {
            if (matcher.TimeScale <= 0) return;

            Position = matcher.GetCellPivot(cell.X, cell.Y - 1).Lerp(matcher.GetCellPivot(cell), Mathf.Clamp(timer / matcher.RuneDropFrame, 0, 1));
            timer += (float)(matcher.TimeScale * Engine.PhysicsTicksPerSecond * delta);
        }

        public void Resize(float size)
        {
            this.size = size;
            QueueRedraw();
        }

        public void Setup(int col, int row)
        {
            cell = new(col, row);
            Position = matcher.GetCellPivot(cell.X, cell.Y - 1);
            SetProcess(false);
        }

        public void Sync(RuneData data)
        {
            if (data == null)
            {
                Visible = false;
                phase = null;
                return;
            }

            Visible = true;
            timer = data.timer;
            phase = data.phase;
            if (type != data.type || level != data.level)
            {
                type = data.type;
                level = data.level;
                StopTween();
                QueueRedraw();
            }

            if (data.IsDroping())
            {
                StopTween();
                Position = matcher.GetCellPivot(cell.X, cell.Y - 1).Lerp(matcher.GetCellPivot(cell), timer / matcher.RuneDropFrame);
                SetProcess(true);
            }
            else if (data.IsIdle())
            {
                InstantBack();
                SetProcess(false);
            }
        }

        public bool Moveable()
        {
            return Visible;
        }

        public void StopTween()
        {
            if (tween?.IsValid() == true)
            {
                tween.Kill();
            }
        }

        public void MoveTo(Vector2 position, double time = 0.1f)
        {
            StopTween();

            tween = CreateTween();
            tween.TweenProperty(this, "position", position, time);
        }

        public void HintTo(Vector2 pos, double time = 0.1f)
        {
            StopTween();

            NodePath position = "position";

            tween = CreateTween();
            tween.TweenProperty(this, position, Position.Lerp(pos, 0.2f), time);
            tween.TweenProperty(this, position, Position, time);
            tween.TweenProperty(this, position, Position.Lerp(pos, 0.2f), time);
            tween.TweenProperty(this, position, Position, time);
            tween.TweenCallback(Callable.From(matcher.Unhint));
        }

        public void Back()
        {
            MoveTo(matcher.GetCellPivot(cell));
        }

        public void InstantBack()
        {
            StopTween();

            Position = matcher.GetCellPivot(cell);
        }

        public void Pick()
        {
            StopTween();
            Scale = 1.25f * Vector2.One;
        }

        public void Unpick()
        {
            Scale = Vector2.One;
            Back();
        }

        public bool PositionAt(int col, int row)
        {
            return cell.X == col && cell.Y == row;
        }
    }
}